From 9c85fccedd2181840e73bdd1f046846cf2b9c909 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 23 Aug 2006 18:38:49 +0100 Subject: [PATCH] [XEN] Restore backwards compatibility by supporting __xen_guest section in dom0 loader. Signed-off-by: Ian Campbell --- xen/arch/x86/domain_build.c | 13 +-- xen/common/elf.c | 163 +++++++++++++++++++++++++++++++++--- xen/include/xen/sched.h | 13 ++- 3 files changed, 166 insertions(+), 23 deletions(-) diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c index 9fecc74ed9..adb903762e 100644 --- a/xen/arch/x86/domain_build.c +++ b/xen/arch/x86/domain_build.c @@ -290,14 +290,7 @@ int construct_dom0(struct domain *d, if ( (rc = parseelfimage(&dsi)) != 0 ) return rc; - if ( dsi.__elfnote_section == NULL ) - { - printk("Not a Xen-ELF image: no Xen ELF notes were found.\n"); - return -EINVAL; - } - - p = xen_elfnote_string(&dsi, XEN_ELFNOTE_PAE_MODE); - dom0_pae = !!(p != NULL && strcmp(p, "yes") == 0); + dom0_pae = (dsi.pae_kernel != PAEKERN_no); xen_pae = (CONFIG_PAGING_LEVELS == 3); if ( dom0_pae != xen_pae ) { @@ -306,8 +299,8 @@ int construct_dom0(struct domain *d, return -EINVAL; } - if ( xen_pae ) - set_bit(VMASST_TYPE_pae_extended_cr3, &d->vm_assist); + if ( xen_pae && dsi.pae_kernel == PAEKERN_extended_cr3 ) + set_bit(VMASST_TYPE_pae_extended_cr3, &d->vm_assist); if ( (p = xen_elfnote_string(&dsi, XEN_ELFNOTE_FEATURES)) != NULL ) { diff --git a/xen/common/elf.c b/xen/common/elf.c index 8cdeb0e737..594060c99b 100644 --- a/xen/common/elf.c +++ b/xen/common/elf.c @@ -22,6 +22,80 @@ static inline int is_loadable_phdr(Elf_Phdr *phdr) ((phdr->p_flags & (PF_W|PF_X)) != 0)); } +/* + * Fallback for kernels containing only the legacy __xen_guest string + * and no ELF notes. + */ +static int is_xen_guest_section(Elf_Shdr *shdr, const char *shstrtab) +{ + return strcmp(&shstrtab[shdr->sh_name], "__xen_guest") == 0; +} + +static const char *xen_guest_lookup(struct domain_setup_info *dsi, int type) +{ + const char *xenguest_fallbacks[] = { + [XEN_ELFNOTE_ENTRY] = "VIRT_ENTRY=", + [XEN_ELFNOTE_HYPERCALL_PAGE] = "HYPERCALL_PAGE=", + [XEN_ELFNOTE_VIRT_BASE] = "VIRT_BASE=", + [XEN_ELFNOTE_PADDR_OFFSET] = "ELF_PADDR_OFFSET=", + [XEN_ELFNOTE_XEN_VERSION] = "XEN_VER=", + [XEN_ELFNOTE_GUEST_OS] = "GUEST_OS=", + [XEN_ELFNOTE_GUEST_VERSION] = "GUEST_VER=", + [XEN_ELFNOTE_LOADER] = "LOADER=", + [XEN_ELFNOTE_PAE_MODE] = "PAE=", + [XEN_ELFNOTE_FEATURES] = "FEATURES=", + [XEN_ELFNOTE_BSD_SYMTAB] = "BSD_SYMTAB=", + }; + const char *fallback; + const char *p; + + if ( type > sizeof(xenguest_fallbacks) ) + return NULL; + + if ( (fallback = xenguest_fallbacks[type]) == NULL ) + return NULL; + + if ( (p = strstr(dsi->__xen_guest_string,fallback)) == NULL ) + return NULL; + + return p + strlen(fallback); +} + +static const char *xen_guest_string(struct domain_setup_info *dsi, int type) +{ + const char *p = xen_guest_lookup(dsi, type); + + /* + * We special case this since the __xen_guest_section treats the + * mere precense of the BSD_SYMTAB string as true or false. + */ + if ( type == XEN_ELFNOTE_BSD_SYMTAB ) + return p ? "yes" : "no"; + + return p; +} + +static unsigned long long xen_guest_numeric(struct domain_setup_info *dsi, + int type, int *defined) +{ + const char *p = xen_guest_lookup(dsi, type); + unsigned long long value; + + if ( p == NULL ) + return 0; + + value = simple_strtoull(p, NULL, 0); + + /* We special case this since __xen_guest_section contains a PFN + * for this field not a virtual address. + */ + if (type == XEN_ELFNOTE_HYPERCALL_PAGE) + value = dsi->v_start + (value<__elfnote_section ) + return xen_guest_string(dsi, type); + note = xen_elfnote_lookup(dsi, type); if ( note == NULL ) return NULL; - DPRINTK("found Xen ELF note type %#x = \"%s\"\n", - type, (char *)ELFNOTE_DESC(note)); - return (const char *)ELFNOTE_DESC(note); } @@ -90,6 +163,9 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi, *defined = 0; + if ( !dsi->__elfnote_section ) + return xen_guest_numeric(dsi, type, defined); + note = xen_elfnote_lookup(dsi, type); if ( note == NULL ) { @@ -105,6 +181,8 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi, *defined = 1; return *(uint64_t*)ELFNOTE_DESC(note); default: + printk("ERROR: unknown data size %#x for numeric type note %#x\n", + note->descsz, type); return 0; } } @@ -146,6 +224,7 @@ int parseelfimage(struct domain_setup_info *dsi) shstrtab = image + shdr->sh_offset; dsi->__elfnote_section = NULL; + dsi->__xen_guest_string = NULL; /* Look for .notes segment containing at least one Xen note */ for ( h = 0; h < ehdr->e_shnum; h++ ) @@ -159,27 +238,73 @@ int parseelfimage(struct domain_setup_info *dsi) break; } - /* Check the contents of the Xen notes. */ - if ( dsi->__elfnote_section ) + /* Fall back to looking for the special '__xen_guest' section. */ + if ( dsi->__elfnote_section == NULL ) + { + for ( h = 0; h < ehdr->e_shnum; h++ ) + { + shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize)); + if ( is_xen_guest_section(shdr, shstrtab) ) + { + dsi->__xen_guest_string = (char *)image + shdr->sh_offset; + break; + } + } + } + + /* Check the contents of the Xen notes or guest string. */ + if ( dsi->__elfnote_section || dsi->__xen_guest_string ) { const char *loader = xen_elfnote_string(dsi, XEN_ELFNOTE_LOADER); const char *guest_os = xen_elfnote_string(dsi, XEN_ELFNOTE_GUEST_OS); const char *xen_version = xen_elfnote_string(dsi, XEN_ELFNOTE_XEN_VERSION); - if ( ( loader == NULL || strcmp(loader, "generic") ) && - ( guest_os == NULL || strcmp(guest_os, "linux") ) ) + if ( ( loader == NULL || strncmp(loader, "generic", 7) ) && + ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) ) { printk("ERROR: Will only load images built for the generic " "loader or Linux images"); return -EINVAL; } - if ( xen_version == NULL || strcmp(xen_version, "xen-3.0") ) + if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) ) { printk("ERROR: Xen will only load images built for Xen v3.0\n"); } } + else + { +#if defined(__x86_64__) || defined(__i386__) + printk("ERROR: Not a Xen-ELF image: " + "No ELF notes or '__xen_guest' section found.\n"); + return -EINVAL; +#endif + } + + /* + * If we have ELF notes then PAE=yes implies that we must support + * the extended cr3 syntax. Otherwise we need to find the + * [extended-cr3] syntax in the __xen_guest string. + */ + dsi->pae_kernel = PAEKERN_no; + if ( dsi->__elfnote_section ) + { + p = xen_elfnote_string(dsi, XEN_ELFNOTE_PAE_MODE); + if ( p != NULL && strncmp(p, "yes", 3) == 0 ) + dsi->pae_kernel = PAEKERN_extended_cr3; + + } + else + { + p = xen_guest_lookup(dsi, XEN_ELFNOTE_PAE_MODE); + if ( p != NULL && strncmp(p, "yes", 3) == 0 ) + { + dsi->pae_kernel = PAEKERN_yes; + if ( !strncmp(p+4, "[extended-cr3]", 14) ) + dsi->pae_kernel = PAEKERN_extended_cr3; + } + } /* Initial guess for v_start is 0 if it is not explicitly defined. */ dsi->v_start = @@ -187,11 +312,24 @@ int parseelfimage(struct domain_setup_info *dsi) if ( !virt_base_defined ) dsi->v_start = 0; - /* We are using the ELF notes interface so the default is 0. */ + /* + * If we are using the legacy __xen_guest section then elf_pa_off + * defaults to v_start in order to maintain compatibility with + * older hypervisors which set padd in the ELF header to + * virt_base. + * + * If we are using the modern ELF notes interface then the default + * is 0. + */ dsi->elf_paddr_offset = xen_elfnote_numeric(dsi, XEN_ELFNOTE_PADDR_OFFSET, &elf_pa_off_defined); if ( !elf_pa_off_defined ) - dsi->elf_paddr_offset = 0; + { + if ( dsi->__elfnote_section ) + dsi->elf_paddr_offset = 0; + else + dsi->elf_paddr_offset = dsi->v_start; + } if ( elf_pa_off_defined && !virt_base_defined ) { @@ -219,6 +357,7 @@ int parseelfimage(struct domain_setup_info *dsi) } dsi->v_kernentry = ehdr->e_entry; + virt_entry = xen_elfnote_numeric(dsi, XEN_ELFNOTE_ENTRY, &virt_entry_defined); if ( virt_entry_defined ) @@ -234,7 +373,7 @@ int parseelfimage(struct domain_setup_info *dsi) } p = xen_elfnote_string(dsi, XEN_ELFNOTE_BSD_SYMTAB); - if ( p != NULL && strcmp(p, "yes") == 0 ) + if ( p != NULL && strncmp(p, "yes", 3) == 0 ) dsi->load_symtab = 1; dsi->v_kernstart = kernstart; diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index e799b15764..b32f1616c6 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -179,13 +179,24 @@ struct domain_setup_info unsigned long v_kernstart; unsigned long v_kernend; unsigned long v_kernentry; +#define PAEKERN_no 0 +#define PAEKERN_yes 1 +#define PAEKERN_extended_cr3 2 + unsigned int pae_kernel; /* Initialised by loader: Private. */ unsigned long elf_paddr_offset; unsigned int load_symtab; unsigned long symtab_addr; unsigned long symtab_len; - /* Indicate whether it's xen specific image */ + /* + * Only one of __elfnote_* or __xen_guest_string will be + * non-NULL. + * + * You should use the xen_elfnote_* accessors below in order to + * pickup the correct one and retain backwards compatibility. + */ void *__elfnote_section, *__elfnote_section_end; + char *__xen_guest_string; }; extern struct vcpu *idle_vcpu[NR_CPUS]; -- 2.30.2